using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace Matchmaking
{
    class MatchmakingServer
    {
        Socket listenSocket;
        Socket[] workerSockets;

        byte[] incomingDataBuffer;

        public void Start()
        {
            Console.WriteLine("Starting the matchmaking server...");
            workerSockets = new Socket[16];
            for (int i = 0; i < workerSockets.Length; i++)
            {
                workerSockets[i] = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            incomingDataBuffer = new byte[1024];

            try
            {
                listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, 10007);
                listenSocket.Bind(ipLocal);

                listenSocket.Listen(4);

                listenSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);

                Console.WriteLine("Listening socket is:" + listenSocket.LocalEndPoint.ToString() + ", isBound:" + listenSocket.IsBound);
            }
            catch (SocketException e)
            {
                Console.WriteLine(e.Message);
            }
        }

        protected void OnClientConnect(IAsyncResult asyn)
        {
            try
            {
                bool bConnected = false;
                for (int i = 0; i < workerSockets.Length && !bConnected; i++)
                {
                    if (!workerSockets[i].IsBound)
                    {
                        workerSockets[i] = listenSocket.EndAccept(asyn);
                        listenSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);

                        Console.WriteLine(workerSockets[i].RemoteEndPoint + ": <New Connection>");

                        WaitForData(i);
                        bConnected = true;
                    }
                }

                if (!bConnected)
                    Console.WriteLine("Can't connect a new client, all sockets are currently used up.");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        protected void WaitForData(int Index)
        {
            workerSockets[Index].BeginReceive(incomingDataBuffer, 0, incomingDataBuffer.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), Index);
        }

        protected void OnDataReceived(IAsyncResult asyn)
        {
            int index = (int)asyn.AsyncState;

            try
            {
                int iRx = workerSockets[index].EndReceive(asyn);

                char[] chars = new char[iRx + 1];
                System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
                int charLen = d.GetChars(incomingDataBuffer, 0, iRx, chars, 0);
                System.String szData = new System.String(chars);

                Console.WriteLine(workerSockets[index].RemoteEndPoint + ": " + szData);

                WaitForData(index);
            }
            catch (SocketException se)
            {
                Console.WriteLine(se.Message);
                workerSockets[index].Disconnect(true);
            }
        }
    }
}
